package com.cskaoyan.javase.object.equals4;

/**
 * @description: equals()方法
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

/**
 * 方法的声明:
 * public boolean equals(Object obj)
 * 方法传参传入一个Object 代表只要传入一个对象就可以
 * 作用:指示其他某个对象是否与此对象“相等”。
 * 其他的某个对象:obj 参数中的对象
 * 此对象:调用该方法的对象
 *
 * 怎么判断两个对象相等?
 * 我们的观点:
 *  * 对象当中 无非就是属性和行为:成员变量和成员方法 所以只要这两个都一样的 我们就判断相等
 *  * 1,假设这两个对象根本就不是一个类创建出来的对象,我们认为他们肯定不相等的
 *  * 2,假设两个对象是同类创建出来的
 *  *  他们的行为一定是一样
 *  *  所以只要它们的成员变量的取值一样,我们就认为它们是相等的对象
 *
 *  Object类当中equals()方法
 *  public boolean equals(Object obj) {
 *         return (this == obj);
 *  }
 *  它是用双等号连接两个对象--->Object类当中的equals方法认为只有两个对象的地址值都一样 对象才相等 ==比较的对象的地址值
 *  只要当两个对象严格是同一个对象的时候 才相等
 *
 *  显然object类当中equals方法无法满足需求,需要子类重写该方法
 *  前提:按照我们的想法,我们需要通过比较两个对象的成员变量的取值来判断两个对象是否相等
 *  那么我们必然要在重写的equals方法当中去用成员变量的取值去比较
 *  equals方法重写的几条规则:
 *  - 自反性：对于任何非空引用值 x，x.equals(x) 都应返回 true
 *  自己和自己比返回true 没毛病
 *
 * - 排他性：当比对的不是同种类型的对象或者是一个null时，默认返回false
 * instanceof可以是子类对象
 * getClass 严格本类对象
 *
 * - 对称性：对于任何非空引用值 x 和 y，当且仅当 y.equals(x) 返回 true 时，x.equals(y) 才应返回 true
 *  x=y 那么 y也应该=x
 *  只要我们用成员变量的取值去写equals方法 这里就自动满足了
 *  10 = 10
 *  1000=1000
 *
 * - 传递性：对于任何非空引用值 x、y 和 z，如果 x.equals(y) 返回 true，并且 y.equals(z) 返回 true
 *   - 那么x.equals(z) 应返回 true
 *   x=y y=z那么x=z
 *   只要我们用成员变量的取值去写equals方法 这里就自动满足了
 *
 * - 一致性：对于任何非空引用值 x 和 y，多次调用 x.equals(y) 始终返回 true 或始终返回 false
 *   - 前提是对象上equals 比较中所用的信息没有被修改
 *   多次调用这个equals方法 不会出现上次true 下次false 结果是稳定
 *    只要我们用成员变量的取值去写equals方法 这里就自动满足了
 *
 * - 其实只要按照下述原则重写，就能够满足上面的常规协定
 *   - 若是不同类型的对象，直接认定不相等
 *   - 若是相同类型的对象，认为具有相同的成员变量的两个对象，才是相等的
 *
 *
 *
 *
 *
 */
public class Demo {
    public static void main(String[] args) {
        //创建Star对象
        double var = 0.1;
        double var2 = 1.0/10;
        Star s1 = new Star(18, var);
        Star s2 = new Star(18, var2);
        System.out.println(s1.equals(s2));
        //Star s2 = new Star(18, );
        //System.out.println(s1.equals(s2)); //false
        //Star s3 = s2;
        //System.out.println(s3.equals(s2));
        //System.out.println(s2==s3);
        //System.out.println(s1==s2);


    }
}

class Star {
    int age;
    double salary;

    //没有引用数据类型
    String s;
    Student s2;

    public Star() {
    }

    public Star(int age, double salary) {
        this.age = age;
        this.salary = salary;
    }

    //重写Star类的equals方法

    @Override
    public boolean equals(Object obj) {
        //自反性
        if (obj == this) {
            //==判断的是 是否严格就是一个对象
            return true;
        }
        //排他性
        //怎么判断该对象不是当前类的对象呢?
        //1,getClass 是程序运行中该类唯一的运行时对象,这个是很严格的,必须严格是这个类的对象
        //2,instanceof 判断左边的对象是否是右边类的对象或者子类对象 可以是当前类对象 也可以是子类
        //思考:1,2哪个比较严格?
        if (obj == null || this.getClass() != obj.getClass()) {
            //Condition 'obj==null' is always 'false' when reached
            //当前面obj.getClass() obj可以调方法
            //那么obj一定不是null 所以这里 'obj==null' is always 'false'
            //表示该obj对象不是当前类的对象
            return false;
        }
        //对称性 一致性 传递性 只要用成员变量的取值去做比较,就可以自动满足
        //于是下面立刻马上开始比较成员变量的取值,来判断它们两个对象是否相等
        //这里需要做强制类型转换,把Object-->Star
        //思考:这里需不需要判断是否能够强转?
        //不需要的,因为方法走到这里还没有停止,表示obj对象一定是当前类的对象 所以不需要判断 直接强转
        Star otherStar = (Star) obj;
        //比较age是否 相等 如果不相等 直接返回false
        if (otherStar.age != this.age){
            return false;
        }
        //程序走到这里age必然相等了 最后一个需要比较salary
        //如果还相等表示对象相等,否者对象不相等
        //return otherStar.salary == this.salary;

        //浮点数是有精度问题,所以判断两个浮点数是否相等,不能用== 而应该用
        //Double类和Float类中的compare() 方法：
        //1，调用方式：Double.compare(double d1,double d2)   Float.compare(float f1,float f2)

        //​		如果这两个数字数学意义上相等，则返回 0
        //​		如果前者在数字上小于后者，则返回小于 0 的值
        //​		如果前者在数字上大学后者，则返回大于 0 的值
        return (Double.compare(this.salary,otherStar.salary))== 0; //这两个数如果相等 应该返回0
    }
}

class Student{
    int a;
    int b;
    double c;

    //@Override
    //public boolean equals(Object o) {
    //    if (this == o) return true;
    //    if (!(o instanceof Student)) return false;
    //    Student student = (Student) o;
    //    if (a != student.a) return false;
    //    if (b != student.b) return false;
    //    return Double.compare(student.c, c) == 0;
    //}


    @Override
    public boolean equals(Object o) {
        //if后面不加大括号 代码看起来更优美
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        if (a != student.a) return false;
        if (b != student.b) return false;
        return Double.compare(student.c, c) == 0;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = a;
        result = 31 * result + b;
        temp = Double.doubleToLongBits(c);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
}